篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。 Uniswap Uniswap 是所谓的 Decentrali
篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。
Uniswap
Uniswap 是所谓的 Decentralized Exchange,它允许个人或称为流动性提供者,将 Token 汇集到智能合约中提供流动性。
概述
我们实现了 Uniswap V1,它只在 BSV 和 Token 之间直接交换。如@state 装饰器所示,我们使用一个带有 @state 装饰器 的有状态合约来表示池子。它包含两个 Token :一个用于我们正在交换的 Token (第 7
行),另一个是治理 Token (第 11
行),称为流动性池(LP) Token 。该池将 BSV 直接存储在 UTXO
中(以 satoshis
为单位),将 Token 存储在对应的公钥哈希下(第 3
行)。
contract Uniswap
PubKey poolPubkey;
@state
ERC20 token;
@state
ERC20 lpToken;
...
Uniswap 合约源代码
增加流动性
任何人都可以通过调用函数 addLiquidity
向池中添加流动性。有两种情况(在第 9
行检查):
- 首次增加流动性:可以存入任意数量的 BSV 和 Token 。
- 添加更多流动性: BSV 和 Token 存入的比率必须与池中的现有比率相匹配(第
22
行)。
public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, int lpSenderKeyIndex, int newBsvBalance, SigHashPreimage txPreimage)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
if (oldBsvBalance == 0)
int lpMint = newBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
else
int bsvAmount = newBsvBalance - oldBsvBalance;
require(oldBsvBalance * tokenAmount == bsvAmount * oldTokenBalance);
int lpMint = this.lpToken.totalSupply() * bsvAmount / oldBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));
存入 BSV 后,新的 LP Token 在第 26
行按比例铸造给流动性提供者。 Token 在第 30
行转移到池子对应的账户。
例如,如果池中有 10
个 BSV 和 100
个 LP Token ,而 Alice 又向其中存入了 5
个 BSV ,则将向她铸造 50
个新的 LP Token 。
移除流动性
流动性提供者调用函数 removeLiquidity
来提取他们的资金,包括 BSV 和 Token 。
public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = oldBsvBalance * lpAmount / this.lpToken.totalSupply();
int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();
require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));
require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
int newBsvBalance = oldBsvBalance - bsvAmount;
require(this.propagateState(newBsvBalance, txPreimage));
流动性提供者拥有的 LP Token 的数量来提取流动性(第 9
行和第 10
行)。提款后,LP Token 在第 13
行被烧毁。第 16
行将 Token 从池子中转移到流动性提供者。第 18
行和第 20
行对 BSV 做同样的事情。
请注意,除了合约输出之外,还需要在同一交易中的另一个输出将 BSV 返回给流动性提供者。
BSV -> Token
用户调用函数 swapBsvToToken
将 BSV 兑换成 Token 。在池子收到 BSV 后(在第 7
行计算),第 10
行计算要返回的 Token 数量,第 13
行将它们返回给用户。
public function swapBsvToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int newBsvBalance, SigHashPreimage txPreimage)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = newBsvBalance - oldBsvBalance;
int tokensAmount = this.getAmount(bsvAmount, oldBsvBalance, oldTokenBalance);
require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));
Token -> BSV
用户调用函数 swapTokenToBsv
将 Token 兑换成 BSV 。第 9
行计算要返回的 BSV 数量。第 13
行将 Token 转移到池子中。
public function swapTokenToBsv(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, SigHashPreimage txPreimage)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBsvBalance);
int newBsvBalance = oldBsvBalance - bsvsAmount;
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));
与 removeLiquidity
类似,需要另一个输出来将 BSV 返回给用户。
讨论
我们已经演示了如何在 BSV 区块链上实现一个基本的类似 Uniswap
的合约。有很多方法可以扩展它以使其更实用。
- 价格公式:我们使用以下代码来确定价格,仅基于 BSV 和 Token 储备。它被称为恒定和公式,可能导致池子被排空。为了避免耗尽,可以使用更复杂的公式,如恒定乘积公式 (
x * y = k
),如 Uniswap 中。
function getAmount(int input, int inputReserve, int outputReserve) : int
return outputReserve * input / inputReserve;
- 流动性挖矿:我们可以对每次交换收取费用,并用这些费用来奖励流动性提供者。
- 允许用户直接将一个 Token 换成另一个 Token 。
TokenSwap 实际上已经实现了以上所有以及更多。
致谢
本作品灵感来源于陈诚的这篇文章。